package com.gmrz.service.challengestore;

import com.gmrz.uaf.common.Constants;
import com.gmrz.uaf.protocol.v1.schema.WhiteList;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisConnectionException;

import java.util.HashMap;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.TimeUnit;

public class ChallengeStoreServiceDefaultImpl implements ChallengeStoreService {

	private static final Logger LOG = LogManager.getLogger(ChallengeStoreServiceDefaultImpl.class);
	protected static Cache<String, IChallengeData> cache_;
	protected static Cache<String,String> cache1_;
    protected static Cache<String,List<WhiteList>> cache_whilte_list;
	protected static Cache<String,String> cache2_;
	protected static long duration_;
	protected static int maxRetries_ = 0;
	private static final Object mutex_ = new Object();
	private static int day=365;

	public void init(Properties props) throws ChallengeStoreException {

		try {
			duration_ = Long.parseLong(props.getProperty(
							"mfas.app.challenge.server.nonce.validity.millis",
							"300000"));
			cache_ = CacheBuilder.newBuilder()
					.expireAfterWrite(duration_, TimeUnit.MILLISECONDS).build();
			maxRetries_ = Integer.parseInt(props.getProperty(
					"mfas.auth.max.retry.count", "0"));
		} catch (Exception e) {
			LOG.error("Error initializing " + getClass().getSimpleName());
			throw new ChallengeStoreException("Error initializing "
					+ getClass().getSimpleName(), e);
		}
	}

	public void initCache(Properties props) throws ChallengeStoreException {

		try {
			cache1_ = CacheBuilder.newBuilder()
					.expireAfterWrite(day, TimeUnit.DAYS).build();
			HashMap<String,String> facetids=new HashMap<String,String>();
			cache1_.put("uaf.facet.id",props.getProperty("uaf.facet.id").toString());
		} catch (Exception e) {
			LOG.error("Error initializing " + getClass().getSimpleName());
			throw new ChallengeStoreException("Error initializing "
					+ getClass().getSimpleName(), e);
		}

	}

	public IChallengeData get(String key) throws ChallengeStoreException {
		try {
			return cache_.getIfPresent(key);
		} catch (Exception ex) {
			LOG.error("Unable to get ChallengeData from cache with Key=" + key);
			throw new ChallengeStoreException(
					"Unable to get ChallengeData from cache with Key=" + key,
					ex);
		}
	}

	public String getvalue(String keyvalue) throws IllegalArgumentException, ChallengeStoreException {
		try {
			return cache1_.getIfPresent(keyvalue);
		} catch (Exception ex) {
			LOG.error("Unable to get ChallengeData from cache with Key=" + keyvalue);
			throw new ChallengeStoreException(
					"Unable to get ChallengeData from cache with Key=" + keyvalue, ex);
		}
	}

	public String put(IChallengeData challengeData)
			throws ChallengeStoreException {
		try {

			String key = challengeData.getKey();

			synchronized (mutex_) {
				int count = 0;
				IChallengeData storedData = cache_
						.getIfPresent(key);
				if (storedData == null) {
					count = challengeData.getCount();
					challengeData.incrementCount();
					cache_.put(key, challengeData);
				} else {
					count = storedData.getCount();

					if (count >= maxRetries_ + 1) {
						LOG.error("Max retry limit reached [max=" + maxRetries_
								+ "] [current=" + count
								+ "], considering it Replay Attack");
						throw new ChallengeStoreException(
								"Possible Replay Attack");
					}
					storedData.incrementCount();
					cache_.put(key, storedData);
				}
			}
			return key;
		} catch (Exception ex) {
			LOG.error("Unable to put ChallengeData:" + challengeData);
			throw new ChallengeStoreException("Unable to put ChallengeData:"
					+ challengeData, ex);
		}
	}

	public void putvalue(String key, HashMap<String,String> value) throws ChallengeStoreException {
//		try {
//			cache1_.put(key,value);
//		} catch (Exception ex) {
//			LOG.error("Unable to get ChallengeData from cache with Key=" + key+"value="+value);
//			throw new ChallengeStoreException(
//					"Unable to get ChallengeData from cache with Key=" + key+"value="+value,
//					ex);
//		}
	}

	@Override
	public List<String> getvalue(String key,String[] fields) throws ChallengeStoreException {
		return null;
	}
		public void putString(String key, String value) throws ChallengeStoreException {
		try {
			cache1_.put(key,value);
		} catch (Exception ex) {
			LOG.error("Unable to put cacheData to cache with Key=" + key+"value="+value);
			throw new ChallengeStoreException(
					"Unable to put cacheData to cache with Key=" + key+"value="+value,
					ex);
		}
	}

	public String update(IChallengeData challengeData)
			throws ChallengeStoreException {
		try {
			String key = challengeData.getKey();

			synchronized (mutex_) {
				cache_.put(key, challengeData);
			}
			return key;
		} catch (Exception ex) {
			LOG.error("Unable to put ChallengeData:" + challengeData);
			throw new ChallengeStoreException("Unable to put ChallengeData:"
					+ challengeData, ex);
		}
	}

	public void delete(String key) throws ChallengeStoreException {
		try {
			cache_.invalidate(key);
		} catch (Exception ex) {
			throw new ChallengeStoreException(
					"Unable to remove ChallengeData with Key = " + key, ex);
		}
	}

	public void shutdown() throws ChallengeStoreException {
		LOG.debug("Shutting down the Challenge Store service");
		cache_ = null;
		cache1_=null;
	}

    public List<WhiteList> getWhiteList(String key) throws ChallengeStoreException {

        try {
			if(cache_whilte_list!=null)
                return cache_whilte_list.getIfPresent(key);
			return null;
        } catch (Exception ex) {
            LOG.error("Unable to get white list from cache with Key=" + key,ex);
            throw new ChallengeStoreException(
                    "Unable to get white list  from cache with Key=" + key, ex);
        }
    }

    public void setWhiteList(String key, List<WhiteList> list) throws ChallengeStoreException {
        try {
            cache_whilte_list = CacheBuilder.newBuilder()
                    .expireAfterWrite(day, TimeUnit.DAYS).build();
            cache_whilte_list.put(Constants.WHITE_LIST_KEY,list);
        } catch (Exception e) {
            LOG.error("Error initialzing " + getClass().getSimpleName());
            throw new ChallengeStoreException("Error initialzing "
                    + getClass().getSimpleName(), e);
        }

    }

	@Override
	public String getRedisValue(String key) throws ChallengeStoreException {

		if(cache2_!=null)
			return cache2_.getIfPresent(key);
		return null;
	}

	@Override
	public void setRedisValue(String key, int seconds, String value) throws ChallengeStoreException {
		if(cache2_==null) {
			cache2_ = CacheBuilder.newBuilder()
					.expireAfterWrite(seconds, TimeUnit.SECONDS).build();
		}
		cache2_.put(key,value);
	}

	@Override
	public void setrangeRedisValue(String key, String value) throws ChallengeStoreException {
		if(cache2_==null) {
			cache2_ = CacheBuilder.newBuilder()
					.expireAfterWrite(day, TimeUnit.DAYS).build();
		}
		cache2_.put(key,value);
	}

	@Override
	public void deleteRedisValue(String key) throws ChallengeStoreException {
		cache2_.invalidate(key);
	}

	@Override
	public void sadd(String key, String... members) throws ChallengeStoreException {

	}

	@Override
	public Set<String> smembers(String key) throws ChallengeStoreException {
		return null;
	}

	@Override
	public boolean tryGetDistributedLock(String lockKey, String requestId, int expireTime) throws ChallengeStoreException {
		return false;
	}

	@Override
	public boolean releaseDistributedLock(String lockKey, String requestId) throws ChallengeStoreException {
		return false;
	}

	@Override
	public void updateCertNumCache() throws ChallengeStoreException {

	}
	public long incr(String key) throws ChallengeStoreException{
		return 0;
	}
	public long decr(String key) throws ChallengeStoreException{
		return 0;
	}
	public void deleteAll(String key) throws ChallengeStoreException{

	}
	public long expire(String key,int seconds) throws ChallengeStoreException{
		return 0;
	}
}
